package cn.shengchao.examstar.ai.application.service;

import cn.shengchao.examstar.ai.acl.ports.ICourseWatchTimeService;
import cn.shengchao.examstar.ai.acl.ports.ISpecificationTypeService;
import cn.shengchao.examstar.ai.application.dto.KnowledgePointDTO;
import cn.shengchao.examstar.ai.application.dto.StudyDayDTO;
import cn.shengchao.examstar.ai.application.dto.StudyPlanListDTO;
import cn.shengchao.examstar.ai.application.dto.StudyPlanSummaryDTO;
import cn.shengchao.examstar.ai.domain.model.StudyPlan;
import cn.shengchao.examstar.ai.domain.model.studyplan.StudyPlanId;
import cn.shengchao.examstar.ai.domain.repository.IAiStudyPlanRepository;
import cn.shengchao.examstar.ai.domain.valueobject.*;
import cn.shengchao.examstar.ai.infrastructure.external.ai.service.DifyChatflowClientService;
import cn.shengchao.examstar.ai.infrastructure.external.ai.service.res.StudyPlanDify;
import cn.shengchao.examstar.knowledge.shared.dto.MasteryAnalysisDTO;
import cn.shengchao.examstar.knowledge.shared.service.KnowledgePointSharedService;
import cn.shengchao.examstar.shared.application.ai.AiStudyPlanAppService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * AI学习计划应用服务
 * 协调领域服务和仓储，处理应用层逻辑
 * 同时实现共享接口，适配原有服务
 */
@Service
@Slf4j
public class AiStudyPlanService implements AiStudyPlanAppService {

    @Autowired
    private IAiStudyPlanRepository aiStudyPlanRepository;

    @Autowired
    private IAiStudyPlanRepository studyPlanRepository;

    @Autowired
    private KnowledgePointSharedService knowledgePointSharedService;

    @Autowired
    private ICourseWatchTimeService courseWatchTimeService;

    @Autowired
    private AiStudyDayService aiStudyDayService;

    @Autowired
    private ISpecificationTypeService specificationTypeService;

    @Autowired
    private DifyChatflowClientService difyChatflowClientService;


    /**
     * 判断指定用户在指定类目下是否存在学习计划
     *
     * @param specificationType 类目ID
     * @param userId            用户ID
     * @return 是否存在学习计划
     */
    public boolean existsUserStudyPlanBySpecificationType(Long specificationType, Long userId) {
        if (specificationType == null || userId == null) {
            log.warn("类目ID或用户ID为空，返回false");
            return false;
        }

        boolean exists = studyPlanRepository.existsByUserIdAndSpecificationType(UserId.of(userId), SpecificationType.of(specificationType));
        log.info("用户[{}]在类目[{}]下学习计划存在状态: {}", userId, specificationType, exists);
        return exists;
    }

    /**
     * 创建学习计划（使用领域对象）
     */
    @Transactional
    public void createStudyPlan(Long specificationType,
                                Long userId,
                                String degreeCode,
                                Double dailyHours,
                                LocalDateTime startTime,
                                LocalDateTime endTime,
                                String studyAdvice,
                                List<?> scheduleItems) {

        SpecificationType specType = SpecificationType.of(specificationType);
        DegreeOfMastery degree = DegreeOfMastery.fromCode(degreeCode);
        StudyDuration duration = StudyDuration.ofHours(dailyHours);
        StudyPeriod period = StudyPeriod.of(startTime, endTime);

        if (studyPlanRepository.existsByUserIdAndSpecificationType(UserId.of(userId), specType)) {
            return;
        }

        StudyPlan studyPlan = StudyPlan.create(
                specType,
                UserId.of(userId),
                degree,
                duration,
                period,
                studyAdvice
        );

        // 保存学习计划
        StudyPlan savedPlan = studyPlanRepository.save(studyPlan);
        log.info("成功创建学习计划，ID: {}, 用户: {}, 类目: {}",
                savedPlan.getId(), userId, specificationType);

        // 处理学习日程列表
        if (scheduleItems != null && !scheduleItems.isEmpty()) {
            saveStudyDays(scheduleItems, userId, savedPlan.getId().getValue());
        }
    }

    /**
     * 保存学习日程
     *
     * @param scheduleItems 学习日程列表
     * @param userId        用户ID
     * @param studyPlanId   学习计划ID
     */
    @SuppressWarnings("unchecked")
    private void saveStudyDays(List<?> scheduleItems, Long userId, Long studyPlanId) {
        log.info("开始保存学习日程，学习日程数量: {}", scheduleItems.size());

        try {
            // 检查类型并转换
            List<StudyDayDTO> studyDayDTOs;

            if (scheduleItems.get(0) instanceof StudyDayDTO) {
                studyDayDTOs = (List<StudyDayDTO>) scheduleItems;
            } else {
                // 假设这里的scheduleItems是StudyDay对象的List
                studyDayDTOs = new ArrayList<>();
                log.warn("学习日程类型不匹配，尝试手动转换");

                // 手动处理日程列表
                for (Object item : scheduleItems) {
                    Map<String, Object> itemMap = objectToMap(item);

                    StudyDayDTO dto = StudyDayDTO.builder()
                            .date((LocalDateTime) itemMap.getOrDefault("date", LocalDateTime.now()))
                            .jobName((String) itemMap.getOrDefault("jobName", "未命名任务"))
                            .knowledgePointId(itemMap.get("knowledgePointId") != null ?
                                    String.valueOf(itemMap.get("knowledgePointId")) : null)
                            .knowledgePoint((String) itemMap.getOrDefault("knowledgePoint", ""))
                            .studyTimeMinutes((Integer) itemMap.getOrDefault("studyTimeMinutes", 30))
                            .studyPlanId(String.valueOf(studyPlanId))
                            .userId(String.valueOf(userId))
                            .completed((Boolean) itemMap.getOrDefault("completed", false))
                            .build();

                    studyDayDTOs.add(dto);
                }
            }

            // 确保所有日程有正确的用户ID和学习计划ID
            for (StudyDayDTO dto : studyDayDTOs) {
                if (dto.getUserId() == null) {
                    dto.setUserId(String.valueOf(userId));
                }
                if (dto.getStudyPlanId() == null) {
                    dto.setStudyPlanId(String.valueOf(studyPlanId));
                }
            }

            // 批量创建学习日程
            List<StudyDayDTO> savedStudyDays = aiStudyDayService.batchCreateStudyDays(studyDayDTOs);
            log.info("成功保存学习日程，数量: {}", savedStudyDays.size());
        } catch (Exception e) {
            log.error("保存学习日程失败", e);
            throw new RuntimeException("保存学习日程失败: " + e.getMessage(), e);
        }
    }

    /**
     * 将对象转换为Map
     */
    private Map<String, Object> objectToMap(Object obj) {
        if (obj == null) {
            return Collections.emptyMap();
        }

        try {
            Map<String, Object> result = new HashMap<>();

            // 使用反射获取对象属性
            java.lang.reflect.Field[] fields = obj.getClass().getDeclaredFields();
            for (java.lang.reflect.Field field : fields) {
                field.setAccessible(true);
                result.put(field.getName(), field.get(obj));
            }

            return result;
        } catch (Exception e) {
            log.error("对象转Map失败", e);
            return Collections.emptyMap();
        }
    }

    /**
     * 获取学习计划摘要信息
     *
     * @param userId            用户ID
     * @param specificationType 类目类型
     * @return 学习计划摘要DTO
     */
    public StudyPlanSummaryDTO getStudyPlanSummary(Long userId, Long specificationType) {
        log.info("获取用户[{}]在类目[{}]下的学习计划摘要", userId, specificationType);

        // 1. 查询该类目是否有学习计划
        boolean hasStudyPlan = existsUserStudyPlanBySpecificationType(specificationType, userId);
        if (!hasStudyPlan) {
            return StudyPlanSummaryDTO.builder()
                    .hasStudyPlan(false)
                    .build();
        }

        // 2. 获取学习计划ID
        Optional<StudyPlan> studyPlan = getStudyPlanIdBySpecificationType(userId, specificationType);
        if (studyPlan.isEmpty()) {
            return StudyPlanSummaryDTO.builder()
                    .hasStudyPlan(false)
                    .build();
        }

        // 3. 获取知识点掌握度分析数据
        MasteryAnalysisDTO masteryAnalysis = knowledgePointSharedService.getMasteryAnalysis(
                userId, specificationType, null);
        masteryAnalysis.setLearningRecommendation(studyPlan.get().getStudyAdvice());
        // 3. 获取最薄弱的知识点
        List<KnowledgePointDTO> weakestPoints = masteryAnalysis.getWeakestPoints().stream()
                .limit(2) // 只取前两个最薄弱的知识点
                .map(weak -> KnowledgePointDTO.builder()
                        .id(String.valueOf(weak.getId()))
                        .name(weak.getName())
                        .masteryPercentage(weak.getMasteryPercentage())
                        .masteryLevel(weak.getMasteryLevel())
                        .learningCount(weak.getLearningCount())
                        .studyTimeMinutes(weak.getStudyTimeMinutes())
                        .studyPlanId(String.valueOf(studyPlan.get().getId().getValue()))
                        .build())
                .collect(Collectors.toList());
        // 4. 获取课程学习时长
        int totalStudyTimeMinutes = 0;
        try {
            // 通过防腐层接口获取课程观看时长（秒）
            Long totalWatchLengthSeconds = courseWatchTimeService.getTotalWatchLengthBySpecificationType(userId, specificationType);

            if (totalWatchLengthSeconds != null && totalWatchLengthSeconds > 0) {
                // 转换为分钟（向上取整）
                totalStudyTimeMinutes = (int) Math.ceil(totalWatchLengthSeconds / 60.0);
                log.info("用户[{}]在类目[{}]下的总观看时长：{}秒，约{}分钟",
                        userId, specificationType, totalWatchLengthSeconds, totalStudyTimeMinutes);
            }
        } catch (Exception e) {
            log.error("获取课程观看时长失败", e);
        }

        // 5. 构建并返回摘要DTO
        return StudyPlanSummaryDTO.builder()
                .specificationTypeId(String.valueOf(specificationType))
                .userId(String.valueOf(userId))
                .totalKnowledgePoints(masteryAnalysis.getTotalKnowledgePoints())
                .learnedKnowledgePoints(masteryAnalysis.getLearnedKnowledgePoints())
                .totalStudyTimeMinutes(totalStudyTimeMinutes)
                .masteryPercentage(masteryAnalysis.getOverallMasteryPercentage())
                .masteryLevel(masteryAnalysis.getOverallMasteryLevel())
                .hasStudyPlan(hasStudyPlan)
                .weakestKnowledgePoints(weakestPoints)
                .learningRecommendation(masteryAnalysis.getLearningRecommendation())
                .build();
    }


    /**
     * 根据用户ID和类目获取学习计划ID
     */
    private Optional<StudyPlan> getStudyPlanIdBySpecificationType(Long userId, Long specificationType) {
        // 查询条件
        List<StudyPlan> studyPlans = aiStudyPlanRepository.findBySpecificationType(SpecificationType.of(specificationType));
        // 过滤符合用户ID的计划
        return studyPlans.stream()
                .filter(plan -> plan.getUserId().getValue().equals(userId))
                .findFirst();
    }

    /**
     * 获取用户的所有学习计划列表
     *
     * @param userId 用户ID
     * @return 学习计划列表，包含类目ID和类目名称
     */
    public List<StudyPlanListDTO> getUserStudyPlanList(Long userId) {
        log.info("获取用户[{}]的所有学习计划列表", userId);

        // 1. 获取用户的所有学习计划
        List<StudyPlan> studyPlans = studyPlanRepository.findByUserId(UserId.of(userId));

        if (studyPlans.isEmpty()) {
            log.info("用户[{}]没有学习计划", userId);
            return Collections.emptyList();
        }

        log.info("用户[{}]的学习计划数量: {}", userId, studyPlans.size());

        // 2. 收集所有的类目ID
        Set<Long> specificationTypeIds = studyPlans.stream()
                .map(plan -> plan.getSpecificationType().getValue())
                .collect(Collectors.toSet());

        // 3. 一次性批量获取所有类目信息
        Map<Long, String> specificationNameMap = getSpecificationNames(specificationTypeIds);

        // 4. 转换为DTO
        return studyPlans.stream().map(studyPlan -> {
            // 从领域对象中提取所需信息
            Long specTypeId = studyPlan.getSpecificationType().getValue();
            return StudyPlanListDTO.builder()
                    .id(studyPlan.getId().getValue())
                    .specificationType(specTypeId)
                    // 从Map中获取类目名称
                    .specificationName(specificationNameMap.getOrDefault(specTypeId, "未知类目"))
                    .createTime(studyPlan.getCreateTime())
                    .build();
        }).collect(Collectors.toList());
    }

    /**
     * 批量获取类目名称
     *
     * @param specificationTypeIds 类目ID集合
     * @return 类目ID到类目名称的映射
     */
    private Map<Long, String> getSpecificationNames(Set<Long> specificationTypeIds) {
        try {
            // 通过防腐层获取类目名称
            return specificationTypeService.getSpecificationTypeNames(specificationTypeIds);
        } catch (Exception e) {
            log.error("批量获取类目名称失败", e);
            // 发生异常时返回简单的ID映射
            return specificationTypeIds.stream()
                    .collect(Collectors.toMap(
                            id -> id,
                            id -> "类目" + id
                    ));
        }
    }

    /**
     * 获取类目名称 - 单个获取，不推荐使用
     *
     * @param specificationTypeId 类目ID
     * @return 类目名称
     * @deprecated 使用批量方法 {@link #getSpecificationNames(Set)} 代替
     */
    @Deprecated
    private String getSpecificationName(Long specificationTypeId) {
        try {
            // 通过防腐层获取类目名称
            return specificationTypeService.getSpecificationTypeName(specificationTypeId);
        } catch (Exception e) {
            log.error("获取类目名称失败", e);
            return "未知类目";
        }
    }

    /**
     * 获取学习计划的完成进度
     *
     * @param studyPlanId 学习计划ID
     * @return 完成进度百分比
     */
    private double getStudyPlanCompletionRate(StudyPlanId studyPlanId) {
        try {
            // 获取该学习计划下的所有学习日程
            List<StudyDayDTO> studyDays = aiStudyDayService.findByStudyPlanId(studyPlanId.getValue().toString());

            if (studyDays.isEmpty()) {
                return 0.0;
            }

            // 计算已完成的学习日程数量
            long completedCount = studyDays.stream()
                    .filter(StudyDayDTO::getCompleted)
                    .count();

            // 计算完成率
            return Math.round((double) completedCount / studyDays.size() * 100) / 100.0;
        } catch (Exception e) {
            log.error("获取学习计划完成进度失败", e);
            return 0.0;
        }
    }

    /**
     * 生成学习计划 - 实现共享接口
     */
    @Override
    public Map<String, Object> generateStudyPlan(List<String> knowledgePoints, Map<String, cn.shengchao.examstar.shared.domain.ai.vo.DegreeOfMastery> currentMasteryLevels) {
        // 适配共享接口到内部实现
        Map<String, Object> result = new HashMap<>();

        // 转换掌握程度值对象
        Map<String, DegreeOfMastery> internalMasteryLevels = currentMasteryLevels.entrySet().stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> {
                            String degreeCode = getDegreeCodeFromPercentage(entry.getValue().getPercentage());
                            return DegreeOfMastery.fromCode(degreeCode);
                        }
                ));

        // 调用内部服务生成学习计划
        // 注：此处简化实现，具体逻辑需要根据实际情况调整
        result.put("knowledgePoints", knowledgePoints);
        result.put("masteryLevels", internalMasteryLevels);
        result.put("success", true);

        return result;
    }

    /**
     * 更新学习进度 - 实现共享接口
     */
    @Override
    public boolean updateStudyProgress(String userId, String knowledgePointId, cn.shengchao.examstar.shared.domain.ai.vo.DegreeOfMastery masteryLevel) {
        try {
            // 适配共享接口到内部实现
            // 转换掌握程度
            String degreeCode = getDegreeCodeFromPercentage(masteryLevel.getPercentage());

            // 这里需要补充实现更新学习进度的具体逻辑
            // TODO: 实现更新学习进度的业务逻辑

            return true;
        } catch (Exception e) {
            log.error("更新学习进度失败", e);
            return false;
        }
    }

    /**
     * 修正学习计划
     * 更新学习计划的学习总结、知识点、学习计划和做题及答案等信息
     *
     * @param learningSummary   学习总结
     * @param knowledge         知识点
     * @param studyPlan         学习计划
     * @param questionAndAnswer 做题及答案
     * @return 是否修正成功
     */
    @Transactional
    public StudyPlanDify updateStudyPlan(
            Long userId,
            String learningSummary,
            String knowledge,
            String studyPlan,
            String questionAndAnswer) throws IOException, InterruptedException {
        // 更新学习计划信息
        return difyChatflowClientService.executeStudyPlanCorrectionWorkflow(userId,
                Map.of("learningSummary", learningSummary,
                        "knowledge", knowledge,
                        "studyPlan", studyPlan,
                        "questionAndAnswer", questionAndAnswer));
    }

    /**
     * 根据掌握程度百分比获取对应的等级代码
     */
    private String getDegreeCodeFromPercentage(Double percentage) {
        if (percentage == null) return "C";

        if (percentage >= 90) return "S";
        else if (percentage >= 80) return "A";
        else if (percentage >= 60) return "B";
        else return "C";
    }
}